home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectDraw / Multimon / multimon.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  33.5 KB  |  966 lines

  1. //-----------------------------------------------------------------------------
  2. // File: Multimon.cpp
  3. //
  4. // Desc: This sample demonstrates the following programming concepts:
  5. //   Writing code for a multi-monitor program that works on both Windows 95 
  6. //     (which does not support multiple monitors) and later Windows versions 
  7. //     (which do support it).
  8. //   Using DirectDrawEnumerateEx to enumerate displays.
  9. //   Working with separate device and focus windows.
  10. //   Creating a video-memory sprite that spans multiple screens using multiple
  11. //     copies of the image data.
  12. //   Creating a system-memory sprite that spans multiple screens using a shared
  13. //     copy of the image data.
  14. //
  15. // Copyright (c) 1998-2001 Microsoft Corporation. All rights reserved.
  16. //-----------------------------------------------------------------------------
  17.  
  18.  
  19.  
  20.  
  21. // Multimon.h implements support of the multimon APIs for when your program
  22. // runs on Windows 95 systems.  You can #include multimon.h in all your source
  23. // files that use multimon APIs, and you should #define COMPILE_MULTIMON_STUBS 
  24. // in only one of your source files.
  25. #define COMPILE_MULTIMON_STUBS 
  26. #include <windows.h>
  27. #include <multimon.h>
  28. #include <ddraw.h>
  29. #include "resource.h"
  30.  
  31.  
  32.  
  33. //-----------------------------------------------------------------------------
  34. // Defines, constants, and global variables
  35. //-----------------------------------------------------------------------------
  36. #define NAME  TEXT("MultiMon DirectDraw Sample")
  37. #define TITLE TEXT("MultiMon DirectDraw Sample")
  38.  
  39.  
  40. // An EnumInfo contains extra enumeration information that is passed
  41. // into the the DDEnumCallbackEx function.
  42. struct EnumInfo
  43. {
  44.     BOOL    bMultimonSupported;
  45.     HRESULT hr;
  46. };
  47.  
  48.  
  49. // A Screen represents one display that images can be drawn to.
  50. struct Screen
  51. {
  52.     GUID                 guid;
  53.     TCHAR                szDesc[200];
  54.     HMONITOR             hmon;
  55.     LPDIRECTDRAW7        pDD;
  56.     LPDIRECTDRAWSURFACE7 pDDSFront;
  57.     LPDIRECTDRAWSURFACE7 pDDSBack;
  58.     Screen*              pScreenNext; // For linked list
  59. };
  60.  
  61.  
  62. // A ScreenSurface holds a DirectDrawSurface that can be used on a 
  63. // particular screen.
  64. struct ScreenSurface
  65. {
  66.     Screen*              pScreen;
  67.     LPDIRECTDRAWSURFACE7 pDDS;
  68.     ScreenSurface*       pScreenSurfaceNext; // For linked list
  69.     // Could add a "last used time" field, which could be used to
  70.     // determine whether this ScreenSurface should be
  71.     // removed to free up video memory for another surface
  72. };
  73.  
  74.  
  75. // A Sprite holds generic information about a drawable image, and
  76. // a linked list of ScreenSurfaces (one per screen).
  77. struct Sprite
  78. {
  79.     TCHAR          szName[64];   // Name of this Sprite
  80.     BOOL           bForceSystem; // If TRUE, don't try to create video memory surfaces
  81.     RECT           rcSrc;        // Dimensions of the image
  82.     RECT           rcDest;       // Destination rectangle to draw image into
  83.     LONG           xVel;         // X-Velocity for animation
  84.     LONG           yVel;         // Y-Velocity for animation
  85.     HBITMAP        hbmImage;     // Loaded bitmap image
  86.     BYTE*          pImageData;   // Sharable pointer to DD surface data
  87.     DDSURFACEDESC2 ddsd;         // Holds pitch and pixel format of pImageData
  88.     ScreenSurface* pScreenSurfaceFirst; // Linked list of ScreenSurfaces
  89. };
  90.  
  91.  
  92. Screen* g_pScreenFirst = NULL;   // Linked list of Screens
  93. Sprite* g_pSprite1     = NULL;   // A sprite that uses video memory where possible
  94. Sprite* g_pSprite2     = NULL;   // A sprite that always uses system memory
  95. HWND    g_hWnd         = NULL;   // Main app focus HWND
  96. BOOL    g_bActive      = TRUE;   // Whether app is actively drawing
  97.  
  98.  
  99.  
  100.  
  101. //-----------------------------------------------------------------------------
  102. // Function prototypes
  103. //-----------------------------------------------------------------------------
  104. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR pCmdLine, INT nCmdShow );
  105. HRESULT CreateFocusWindow( HINSTANCE hInstance );
  106. HRESULT EnumerateScreens( VOID );
  107. BOOL WINAPI DDEnumCallback( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName, VOID* pContext );
  108. BOOL WINAPI DDEnumCallbackEx( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName, VOID* pContext, HMONITOR hmon );
  109. HRESULT InitScreens( VOID );
  110. HRESULT InitSprites( VOID );
  111. HRESULT MainLoop( VOID );
  112. LRESULT CALLBACK WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
  113. VOID UpdateFrame( VOID );
  114. BOOL RectIntersectsMonitor( RECT* prcSrc, HMONITOR hmon, RECT* prcScreen );
  115. HRESULT FindOrBuildScreenSurface( Sprite* pSprite, Screen* pScreen, ScreenSurface** ppScreenSurface );
  116. HRESULT SetupScreenSurfaceDDS( Sprite* pSprite, ScreenSurface* pScreenSurface );
  117. VOID DestroyScreenSurfaces( Sprite* pSprite );
  118. VOID Cleanup( VOID );
  119.  
  120.  
  121.  
  122.  
  123. //-----------------------------------------------------------------------------
  124. // Name: WinMain()
  125. // Desc: Initializes the application, then starts the main application loop.
  126. //-----------------------------------------------------------------------------
  127. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR pCmdLine, INT nCmdShow )
  128. {
  129.     HRESULT hr;
  130.  
  131.     if( FAILED( hr = CreateFocusWindow(hInst) ) )
  132.         return 0;
  133.  
  134.     if( FAILED( hr = EnumerateScreens() ) )
  135.         return 0;
  136.  
  137.     if( FAILED( hr = InitScreens() ) )
  138.         return 0;
  139.  
  140.     if( FAILED( hr = InitSprites() ) )
  141.         return 0;
  142.  
  143.     if( FAILED( hr = MainLoop() ) )
  144.         return 0;
  145.  
  146.     return 0;
  147. }
  148.  
  149.  
  150.  
  151.  
  152. //-----------------------------------------------------------------------------
  153. // Name: CreateFocusWindow()
  154. // Desc: Creates the focus window, which is the window which will receive user 
  155. //       input.
  156. //-----------------------------------------------------------------------------
  157. HRESULT CreateFocusWindow( HINSTANCE hInstance )
  158. {
  159.     WNDCLASS wc;
  160.  
  161.     wc.style = CS_HREDRAW | CS_VREDRAW;
  162.     wc.lpfnWndProc = WindowProc;
  163.     wc.cbClsExtra = 0;
  164.     wc.cbWndExtra = 0;
  165.     wc.hInstance = hInstance;
  166.     wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(IDI_ICON1) );
  167.     wc.hCursor = LoadCursor( NULL, IDC_ARROW );
  168.     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  169.     wc.lpszMenuName = NULL;
  170.     wc.lpszClassName = NAME;
  171.     RegisterClass( &wc );
  172.     
  173.     // Window style and dimensions are not important, 
  174.     // since they will be adjusted elsewhere.
  175.     g_hWnd = CreateWindowEx( 0, NAME, TITLE, 0, 0, 0, 0, 0, 
  176.                              NULL, NULL, hInstance, NULL );
  177.  
  178.     // It is important to call ShowWindow on this window to ensure 
  179.     // that any activity from other windows is obscured.
  180.     ShowWindow( g_hWnd, SW_SHOW );
  181.  
  182.     if( g_hWnd == NULL )
  183.         return E_FAIL;
  184.  
  185.     return S_OK;
  186. }
  187.  
  188.  
  189.  
  190.  
  191. //-----------------------------------------------------------------------------
  192. // Name: EnumerateScreens()
  193. // Desc: Creates a Screen structure for every appropriate screen in the user's
  194. //       computer.  
  195. //-----------------------------------------------------------------------------
  196. HRESULT EnumerateScreens()
  197. {
  198.     HRESULT  hr;
  199.     EnumInfo enumInfo;
  200.  
  201.     ZeroMemory( &enumInfo, sizeof(enumInfo) );
  202.     enumInfo.bMultimonSupported = TRUE;
  203.  
  204.     if( FAILED( hr = DirectDrawEnumerateEx( DDEnumCallbackEx, 
  205.                                             &enumInfo, 
  206.                                             DDENUM_ATTACHEDSECONDARYDEVICES ) ) )
  207.         return hr;
  208.  
  209.     // If something failed inside the enumeration, be sure to return that HRESULT
  210.     if( FAILED(enumInfo.hr) )
  211.         return enumInfo.hr;
  212.  
  213.     return S_OK;
  214. }
  215.  
  216.  
  217.  
  218.  
  219. //-----------------------------------------------------------------------------
  220. // Name: DDEnumCallbackEx()
  221. // Desc: This callback function is called by DirectDraw once for each 
  222. //       available DirectDraw device.  In this implementation, it saves the
  223. //       GUID, device description, and hmon in a Screen structure for later use.
  224. //-----------------------------------------------------------------------------
  225. BOOL WINAPI DDEnumCallbackEx( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName,
  226.                               VOID* pContext, HMONITOR hmon )
  227. {
  228.     Screen*   pScreenNew;
  229.     EnumInfo* pEnumInfo = (EnumInfo*)pContext;
  230.     GUID      guidNull;
  231.  
  232.     ZeroMemory( &guidNull, sizeof(GUID) );
  233.     if( g_pScreenFirst != NULL && 
  234.         g_pScreenFirst->guid == guidNull )
  235.     {
  236.         // We must be running on a multimon system, so get rid of the 
  237.         // guidNull Screen -- we want Screens with specific GUIDs.
  238.         delete g_pScreenFirst;
  239.         g_pScreenFirst = NULL;
  240.     }
  241.  
  242.     // Store all the info in a Screen structure
  243.     pScreenNew = new Screen;
  244.     if( pScreenNew == NULL )
  245.     {
  246.         pEnumInfo->hr = E_OUTOFMEMORY;
  247.         return FALSE; // fatal error, stop enumerating
  248.     }
  249.  
  250.     ZeroMemory( pScreenNew, sizeof(Screen) );
  251.     if( pGuid == NULL )
  252.         pScreenNew->guid = guidNull;
  253.     else
  254.         pScreenNew->guid = *pGuid;
  255.  
  256.     lstrcpy( pScreenNew->szDesc, pszDesc );
  257.     pScreenNew->hmon = hmon;
  258.  
  259.     // Insert Screen into global linked list
  260.     if( g_pScreenFirst == NULL )
  261.     {
  262.         g_pScreenFirst = pScreenNew;
  263.     }
  264.     else
  265.     {
  266.         // Insert at end of list
  267.         Screen* pScreen = g_pScreenFirst; 
  268.         while( pScreen->pScreenNext != NULL )
  269.             pScreen = pScreen->pScreenNext;
  270.  
  271.         pScreen->pScreenNext = pScreenNew;
  272.     }
  273.  
  274.     return TRUE; // Keep enumerating
  275. }
  276.  
  277.  
  278.  
  279.  
  280. //-----------------------------------------------------------------------------
  281. // Name: InitScreens()
  282. // Desc: For each Screen, this function initializes DirectDraw and sets up the 
  283. //       front buffer, back buffer, and a clipper.  Many fullscreen DirectDraw
  284. //       programs don't need to create clippers, but this one does so to allow
  285. //       the sprites to be automatically clipped to each display.
  286. //-----------------------------------------------------------------------------
  287. HRESULT InitScreens( VOID )
  288. {
  289.     HRESULT             hr;
  290.     Screen*             pScreen = NULL;
  291.     GUID*               pGuid   = NULL;
  292.     DWORD               dwFlags;
  293.     DDSURFACEDESC2      ddsd;
  294.     LPDIRECTDRAWCLIPPER pClip   = NULL;
  295.     DDSCAPS2            ddsCaps;
  296.     RECT                rc;
  297.     HRGN                hrgn;
  298.     BYTE                rgnDataBuffer[1024];
  299.     GUID                guidNull;
  300.  
  301.     ZeroMemory( &guidNull, sizeof(GUID) );
  302.  
  303.     for( pScreen = g_pScreenFirst; pScreen != NULL; pScreen = pScreen->pScreenNext )
  304.     {
  305.         if( pScreen->guid == guidNull )
  306.             pGuid = NULL;
  307.         else
  308.             pGuid = &pScreen->guid;
  309.  
  310.         if( FAILED(hr = DirectDrawCreateEx( pGuid, (VOID**) &(pScreen->pDD), 
  311.                                             IID_IDirectDraw7, NULL ) ) )
  312.             return hr;
  313.  
  314.         if( pScreen == g_pScreenFirst)
  315.         {
  316.             dwFlags = DDSCL_SETFOCUSWINDOW;
  317.             if( FAILED( hr = pScreen->pDD->SetCooperativeLevel( g_hWnd, dwFlags ) ) )
  318.                 return hr;
  319.  
  320.             dwFlags = DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
  321.             if( FAILED( hr = pScreen->pDD->SetCooperativeLevel(g_hWnd, dwFlags ) ) )
  322.                 return hr;
  323.         }
  324.         else
  325.         {
  326.             dwFlags = DDSCL_SETFOCUSWINDOW | DDSCL_CREATEDEVICEWINDOW |
  327.                       DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
  328.             if( FAILED( hr = pScreen->pDD->SetCooperativeLevel(g_hWnd, dwFlags ) ) )
  329.                 return hr;
  330.         }
  331.  
  332.         if( FAILED( hr = pScreen->pDD->SetDisplayMode( 640, 480, 16, 0, 0 ) ) )
  333.             return hr;
  334.     }
  335.  
  336.     // Note: It is recommended that programs call SetDisplayMode on all screens
  337.     // before creating/acquiring any DirectDrawSurfaces.
  338.     for( pScreen = g_pScreenFirst; pScreen != NULL; pScreen = pScreen->pScreenNext )
  339.     {
  340.         ZeroMemory( &ddsd, sizeof(ddsd) );
  341.         ddsd.dwSize            = sizeof(ddsd);
  342.         ddsd.dwFlags           = DDSD_BACKBUFFERCOUNT | DDSD_CAPS;
  343.         ddsd.dwBackBufferCount = 1;
  344.         ddsd.ddsCaps.dwCaps    = DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_PRIMARYSURFACE;
  345.  
  346.         if( FAILED( hr = pScreen->pDD->CreateSurface( &ddsd, 
  347.                                                       &pScreen->pDDSFront, NULL ) ) )
  348.         {
  349.             return hr;
  350.         }
  351.  
  352.  
  353.         ZeroMemory( &ddsCaps, sizeof(ddsCaps) );
  354.         ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
  355.         if( FAILED( hr = pScreen->pDDSFront->GetAttachedSurface( &ddsCaps, 
  356.                                                                  &pScreen->pDDSBack ) ) )
  357.             return hr;
  358.  
  359.         ZeroMemory( &ddsd, sizeof(ddsd) );
  360.         ddsd.dwSize = sizeof(ddsd);
  361.         if( FAILED( hr = pScreen->pDDSFront->GetSurfaceDesc( &ddsd ) ) )
  362.             return hr;
  363.  
  364.         SetRect( &rc, 0, 0, ddsd.dwWidth, ddsd.dwHeight );
  365.         hrgn = CreateRectRgn( 0, 0, ddsd.dwWidth, ddsd.dwHeight );
  366.         GetRegionData( hrgn, sizeof(rgnDataBuffer), (RGNDATA*)rgnDataBuffer );
  367.         DeleteObject( hrgn );
  368.  
  369.         if( FAILED( hr = pScreen->pDD->CreateClipper( 0, &pClip, 0 ) ) )
  370.             return hr;
  371.  
  372.         if( FAILED( hr = pClip->SetClipList( (RGNDATA*)rgnDataBuffer, 0 ) ) )
  373.         {
  374.             pClip->Release();
  375.             return hr;
  376.         }
  377.  
  378.         if( FAILED( hr = pScreen->pDDSFront->SetClipper( pClip ) ) )
  379.         {
  380.             pClip->Release();
  381.             return hr;
  382.         }
  383.  
  384.         if( FAILED( hr = pScreen->pDDSBack->SetClipper( pClip ) ) )
  385.         {
  386.             pClip->Release();
  387.             return hr;
  388.         }
  389.  
  390.         pClip->Release();
  391.     }
  392.  
  393.     return S_OK;
  394. }
  395.  
  396.  
  397.  
  398.  
  399. //-----------------------------------------------------------------------------
  400. // Name: InitSprites()
  401. // Desc: Initializes the objects that will be drawn and animated.  Note that
  402. //       the ScreenSurfaces are created when they are first needed, not here.
  403. //-----------------------------------------------------------------------------
  404. HRESULT InitSprites( VOID )
  405. {
  406.     BITMAP bm;
  407.  
  408.     // Initialize the first Sprite.  This sprite will try to use video memory
  409.     // on each Screen.
  410.     g_pSprite1 = new Sprite;
  411.     if( g_pSprite1 == NULL )
  412.         return E_OUTOFMEMORY;
  413.  
  414.     ZeroMemory( g_pSprite1, sizeof(Sprite) );
  415.     lstrcpy( g_pSprite1->szName, TEXT("Sprite 1") );
  416.     g_pSprite1->bForceSystem = FALSE; // This sprite will try to use video memory
  417.  
  418.     g_pSprite1->hbmImage = (HBITMAP) LoadImage( GetModuleHandle(NULL), 
  419.                                                 MAKEINTRESOURCE(IDB_BITMAP1), 
  420.                                                 IMAGE_BITMAP, 0, 0,
  421.                                                 LR_CREATEDIBSECTION );
  422.     if( g_pSprite1->hbmImage == NULL )
  423.         return E_FAIL;
  424.  
  425.     GetObject( g_pSprite1->hbmImage, sizeof(bm), &bm );  // get size of bitmap
  426.     SetRect( &g_pSprite1->rcSrc, 0, 0, bm.bmWidth, bm.bmHeight );
  427.  
  428.     g_pSprite1->rcDest = g_pSprite1->rcSrc;
  429.     g_pSprite1->xVel = 2; // Animation velocity
  430.     g_pSprite1->yVel = 1;
  431.  
  432.     // Initialize the second Sprite.  This sprite will use system memory (and
  433.     // share that memory between ScreenSurfaces whenever possible).
  434.     g_pSprite2 = new Sprite;
  435.     if( g_pSprite2 == NULL )
  436.         return E_OUTOFMEMORY;
  437.  
  438.     ZeroMemory( g_pSprite2, sizeof(Sprite) );
  439.     lstrcpy(g_pSprite2->szName, TEXT("Sprite 2"));
  440.     g_pSprite2->bForceSystem = TRUE; // This sprite will always use system memory
  441.     g_pSprite2->hbmImage = (HBITMAP) LoadImage( GetModuleHandle(NULL), 
  442.                                                 MAKEINTRESOURCE(IDB_BITMAP2), 
  443.                                                 IMAGE_BITMAP, 0, 0, 
  444.                                                 LR_CREATEDIBSECTION );
  445.     if( g_pSprite2->hbmImage == NULL )
  446.         return E_FAIL;
  447.  
  448.     GetObject( g_pSprite2->hbmImage, sizeof(bm), &bm );  // get size of bitmap
  449.     SetRect( &g_pSprite2->rcSrc, 0, 0, bm.bmWidth, bm.bmHeight );
  450.     g_pSprite2->rcDest = g_pSprite1->rcSrc;
  451.     g_pSprite2->xVel = -1; // Animation velocity
  452.     g_pSprite2->yVel = -2;
  453.  
  454.     return S_OK;
  455. }
  456.  
  457.  
  458.  
  459.  
  460. //-----------------------------------------------------------------------------
  461. // Name: MainLoop()
  462. // Desc: The main window message pump.  When the application is active (not
  463. //       minimized), it uses PeekMessage so that UpdateFrame can be called
  464. //       frequently once all window messages are handled.  When it is not 
  465. //       active, GetMessage is used instead to give more processing time to
  466. //       other running programs.
  467. //-----------------------------------------------------------------------------
  468. HRESULT MainLoop( VOID )
  469. {
  470.     MSG msg;
  471.     BOOL bGotMsg;
  472.  
  473.     while( TRUE )
  474.     {
  475.         if( g_bActive)
  476.             bGotMsg = PeekMessage( &msg, NULL, 0, 0, PM_REMOVE );
  477.         else
  478.             bGotMsg = GetMessage( &msg, NULL, 0, 0);
  479.  
  480.         if( msg.message == WM_QUIT)
  481.             return S_OK;
  482.         
  483.         if( bGotMsg)
  484.         {
  485.             TranslateMessage( &msg ); 
  486.             DispatchMessage( &msg );
  487.         }
  488.         else if( g_bActive)
  489.         {
  490.             UpdateFrame();
  491.         }
  492.     }
  493. }
  494.  
  495.  
  496.  
  497.  
  498. //-----------------------------------------------------------------------------
  499. // Name: WindowProc()
  500. // Desc: Handler for window messages.
  501. //-----------------------------------------------------------------------------
  502. LRESULT CALLBACK WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  503. {
  504.     switch(uMsg)
  505.     {
  506.     case WM_SIZE:
  507.         if( SIZE_MAXHIDE == wParam || SIZE_MINIMIZED == wParam )
  508.         {
  509.             g_bActive = FALSE;
  510.             // Give window an icon and system menu on the taskbar when minimized
  511.             SetWindowLong(hWnd, GWL_STYLE, WS_SYSMENU); 
  512.         }
  513.         else
  514.         {
  515.             g_bActive = TRUE;
  516.             // Remove any window "decoration" when fullscreen
  517.             SetWindowLong(hWnd, GWL_STYLE, WS_POPUP);
  518.         }
  519.  
  520.         return DefWindowProc( hWnd, uMsg, wParam, lParam );
  521.  
  522.     case WM_CLOSE:
  523.         return DefWindowProc( hWnd, uMsg, wParam, lParam );
  524.  
  525.     case WM_CHAR:
  526.         DestroyWindow( hWnd );
  527.         return 0;
  528.  
  529.     case WM_DESTROY:
  530.         Cleanup();
  531.         PostQuitMessage( 0 );
  532.         return 0;
  533.  
  534.     default:
  535.         return DefWindowProc( hWnd, uMsg, wParam, lParam );
  536.     }
  537. }
  538.  
  539.  
  540.  
  541.  
  542. //-----------------------------------------------------------------------------
  543. // Name: UpdateFrame()
  544. // Desc: Renders one frame of animation, then updates the sprites for the next
  545. //       frame.
  546. //-----------------------------------------------------------------------------
  547. VOID UpdateFrame( VOID )
  548. {
  549.     HRESULT        hr;
  550.     Screen*        pScreen;
  551.     RECT           rcScreen;
  552.     ScreenSurface* pScreenSurface;
  553.     DDBLTFX        ddbltfx;
  554.  
  555.     for( pScreen = g_pScreenFirst; pScreen != NULL; pScreen = pScreen->pScreenNext )
  556.     {
  557.         // Handle lost surfaces
  558.         if( pScreen->pDDSFront->IsLost() == DDERR_SURFACELOST )
  559.         {
  560.             // Though surface memory can be reaquired via RestoreAllSurfaces, the
  561.             // image contents will be undefined.  So we destroy all the ScreenSurfaces
  562.             // and let them be recreated as needed.  Calling RestoreAllSurfaces
  563.             // takes care of the remaining surfaces (the front and back buffers).
  564.             DestroyScreenSurfaces( g_pSprite1 );
  565.             DestroyScreenSurfaces( g_pSprite2 );
  566.             hr = pScreen->pDD->RestoreAllSurfaces();
  567.         }
  568.  
  569.         // Clear the back buffer for this Screen
  570.         ZeroMemory( &ddbltfx, sizeof(ddbltfx) );
  571.         ddbltfx.dwSize = sizeof(ddbltfx);
  572.         ddbltfx.dwFillColor = 0; // Black
  573.         hr = pScreen->pDDSBack->Blt( NULL, NULL, NULL, 
  574.                                      DDBLT_COLORFILL | DDBLT_WAIT, 
  575.                                      &ddbltfx );
  576.  
  577.         // Draw first sprite
  578.         if( RectIntersectsMonitor( &g_pSprite1->rcDest, pScreen->hmon, &rcScreen ) )
  579.         {
  580.             hr = FindOrBuildScreenSurface( g_pSprite1, pScreen, &pScreenSurface );
  581.  
  582.             if( SUCCEEDED(hr) )
  583.             {
  584.                 hr = pScreen->pDDSBack->Blt( &rcScreen, pScreenSurface->pDDS, 
  585.                                              &g_pSprite1->rcSrc, 
  586.                                              DDBLT_WAIT, NULL );
  587.             }
  588.         }
  589.  
  590.         // Draw second sprite
  591.         if( RectIntersectsMonitor( &g_pSprite2->rcDest, pScreen->hmon, &rcScreen ) )
  592.         {
  593.             hr = FindOrBuildScreenSurface( g_pSprite2, pScreen, &pScreenSurface );
  594.             if( SUCCEEDED( hr ) )
  595.             {
  596.                 hr = pScreen->pDDSBack->Blt( &rcScreen, pScreenSurface->pDDS, 
  597.                                              &g_pSprite2->rcSrc, DDBLT_WAIT, NULL );
  598.             }
  599.         }
  600.     }
  601.  
  602.     // Flip all screens.  This is done in a separate loop to make the flips happen
  603.     // as close together in time as possible
  604.     for( pScreen = g_pScreenFirst; pScreen != NULL; pScreen = pScreen->pScreenNext )
  605.     {
  606.         hr = pScreen->pDDSFront->Flip( NULL, DDFLIP_WAIT );
  607.     }
  608.  
  609.     // Animate Sprites for the next frame.  The sprites are bounced against the 
  610.     // virtual desktop, which may cause them to partially or totally disappear
  611.     // if your screens are set up in a way that is non-rectangular.  Since the
  612.     // animation is not the purpose of this demo, this simplified approach is used.
  613.     RECT rcDesktop;
  614.     rcDesktop.left   = GetSystemMetrics(SM_XVIRTUALSCREEN);
  615.     rcDesktop.right  = rcDesktop.left + GetSystemMetrics(SM_CXVIRTUALSCREEN);
  616.     rcDesktop.top    = GetSystemMetrics(SM_YVIRTUALSCREEN);
  617.     rcDesktop.bottom = rcDesktop.top + GetSystemMetrics(SM_CYVIRTUALSCREEN);
  618.  
  619.     // Animate first sprite
  620.     OffsetRect( &g_pSprite1->rcDest, g_pSprite1->xVel, g_pSprite1->yVel );
  621.  
  622.     if( ( g_pSprite1->rcDest.right > rcDesktop.right && g_pSprite1->xVel > 0 ) ||
  623.         ( g_pSprite1->rcDest.left < rcDesktop.left && g_pSprite1->xVel < 0 ) )
  624.     {
  625.         g_pSprite1->xVel = -g_pSprite1->xVel;
  626.     }
  627.  
  628.     if( ( g_pSprite1->rcDest.bottom > rcDesktop.bottom && g_pSprite1->yVel > 0 ) ||
  629.         ( g_pSprite1->rcDest.top < rcDesktop.top && g_pSprite1->yVel < 0 ) )
  630.     {
  631.         g_pSprite1->yVel = -g_pSprite1->yVel;
  632.     }
  633.  
  634.     // Animate second sprite
  635.     OffsetRect( &g_pSprite2->rcDest, g_pSprite2->xVel, g_pSprite2->yVel );
  636.     if( ( g_pSprite2->rcDest.right > rcDesktop.right && g_pSprite2->xVel > 0 ) ||
  637.         ( g_pSprite2->rcDest.left < rcDesktop.left  && g_pSprite2->xVel < 0 ) )
  638.     {
  639.         g_pSprite2->xVel = -g_pSprite2->xVel;
  640.     }
  641.  
  642.     if( ( g_pSprite2->rcDest.bottom > rcDesktop.bottom && g_pSprite2->yVel > 0 ) ||
  643.         ( g_pSprite2->rcDest.top < rcDesktop.top && g_pSprite2->yVel < 0 ) )
  644.     {
  645.         g_pSprite2->yVel = -g_pSprite2->yVel;
  646.     }
  647. }
  648.  
  649.  
  650.  
  651.  
  652. //-----------------------------------------------------------------------------
  653. // Name: RectIntersectsMonitor()
  654. // Desc: Returns TRUE if prcSrc intersects the monitor hmon, and uses prcScreen
  655. //       to store prcSrc in that monitor's local coordinate system.
  656. //-----------------------------------------------------------------------------
  657. BOOL RectIntersectsMonitor( RECT* prcSrc, HMONITOR hmon, RECT* prcScreen )
  658. {
  659.     MONITORINFO mi;
  660.     RECT rcIntersection;
  661.     BOOL bIntersects;
  662.  
  663.     ZeroMemory( &mi, sizeof(mi) );
  664.     mi.cbSize = sizeof(mi);
  665.     if( hmon == NULL )
  666.     {
  667.         SetRect( &mi.rcMonitor, 0, 0, GetSystemMetrics(SM_CXSCREEN), 
  668.                  GetSystemMetrics(SM_CYSCREEN) );
  669.     }
  670.     else
  671.     {
  672.         GetMonitorInfo(hmon, &mi);
  673.     }
  674.  
  675.     bIntersects = IntersectRect( &rcIntersection, prcSrc, &mi.rcMonitor );
  676.     if( !bIntersects)
  677.         return FALSE;
  678.  
  679.     *prcScreen = *prcSrc;
  680.     OffsetRect( prcScreen, -mi.rcMonitor.left, -mi.rcMonitor.top );
  681.  
  682.     return TRUE;
  683. }
  684.  
  685.  
  686.  
  687.  
  688. //-----------------------------------------------------------------------------
  689. // Name: FindOrBuildScreenSurface()
  690. // Desc: This is called when UpdateFrame needs to draw the image of a Sprite 
  691. //       onto a particular Screen.  If a ScreenSurface already exists for this
  692. //       Screen and Sprite, a pointer to it is returned.  Otherwise, a new
  693. //       ScreenSurface is created (and stored for future reuse).
  694. //-----------------------------------------------------------------------------
  695. HRESULT FindOrBuildScreenSurface( Sprite* pSprite, Screen* pScreen, 
  696.                                   ScreenSurface** ppScreenSurface )
  697. {
  698.     HRESULT hr;
  699.     ScreenSurface* pScreenSurface;
  700.  
  701.     for( pScreenSurface = pSprite->pScreenSurfaceFirst; 
  702.          pScreenSurface != NULL; 
  703.          pScreenSurface = pScreenSurface->pScreenSurfaceNext )
  704.     {
  705.         if( pScreenSurface->pScreen == pScreen )
  706.         {
  707.             // ScreenSurface exists for this Screen, so return a pointer to it
  708.             *ppScreenSurface = pScreenSurface;
  709.             return S_OK;
  710.         }
  711.     }
  712.  
  713.     // No ScreenSurface for this Screen exists yet, so build one.
  714.     pScreenSurface = new ScreenSurface;
  715.     if( pScreenSurface == NULL )
  716.         return E_OUTOFMEMORY;
  717.  
  718.     ZeroMemory( pScreenSurface, sizeof(ScreenSurface) );
  719.  
  720.     pScreenSurface->pScreen = pScreen;
  721.     if( FAILED( hr = SetupScreenSurfaceDDS(pSprite, pScreenSurface ) ) )
  722.     {
  723.         delete pScreenSurface;
  724.         return hr;
  725.     }
  726.  
  727.     // Insert this new ScreenSurface in the Sprite's list:
  728.     pScreenSurface->pScreenSurfaceNext = pSprite->pScreenSurfaceFirst;
  729.     pSprite->pScreenSurfaceFirst = pScreenSurface;
  730.     *ppScreenSurface = pScreenSurface;
  731.  
  732.     return S_OK;
  733. }
  734.  
  735.  
  736.  
  737.  
  738. //-----------------------------------------------------------------------------
  739. // Name: SetupScreenSurfaceDDS()
  740. // Desc: Generates the DirectDrawSurface for a new ScreenSurface, and draws
  741. //       the appropriate image into it.
  742. //-----------------------------------------------------------------------------
  743. HRESULT SetupScreenSurfaceDDS( Sprite* pSprite, ScreenSurface* pScreenSurface )
  744. {
  745.     HRESULT hr;
  746.     DDSURFACEDESC2 ddsd;
  747.     TCHAR sz[200];
  748.     Screen* pScreen = pScreenSurface->pScreen;
  749.  
  750.     ZeroMemory(&ddsd, sizeof(ddsd));
  751.     ddsd.dwSize = sizeof(ddsd);
  752.     ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
  753.     ddsd.dwWidth = pSprite->rcSrc.right - pSprite->rcSrc.left;
  754.     ddsd.dwHeight = pSprite->rcSrc.bottom - pSprite->rcSrc.top;
  755.     ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
  756.  
  757.     // Try to create the surface in video memory, unless the bForceSystem flag
  758.     // is set on the Sprite.
  759.     if( pSprite->bForceSystem || 
  760.         FAILED( hr = pScreen->pDD->CreateSurface( &ddsd, &pScreenSurface->pDDS, NULL ) ) )
  761.     {
  762.         // Either this sprite has the bForceSystem flag, or creation in video
  763.         // memory failed, so try to create the surface in system memory.
  764.         ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
  765.  
  766.         if( FAILED( hr = pScreen->pDD->CreateSurface( &ddsd, &pScreenSurface->pDDS, NULL ) ) )
  767.             return hr;
  768.     }
  769.  
  770.     if( ddsd.ddsCaps.dwCaps == DDSCAPS_SYSTEMMEMORY && 
  771.         pSprite->pImageData != NULL )
  772.     {
  773.         // See if we can reuse the image data that is stored in the Sprite.
  774.         // As long as the pixel formats match, the image is reusable.
  775.         ZeroMemory( &ddsd, sizeof(ddsd) );
  776.         ddsd.dwSize = sizeof(ddsd);
  777.  
  778.         if( FAILED( hr = pScreenSurface->pDDS->GetSurfaceDesc( &ddsd ) ) )
  779.             return hr;
  780.  
  781.         if( ddsd.ddpfPixelFormat.dwRGBBitCount == pSprite->ddsd.ddpfPixelFormat.dwRGBBitCount &&
  782.             ddsd.ddpfPixelFormat.dwRBitMask    == pSprite->ddsd.ddpfPixelFormat.dwRBitMask &&
  783.             ddsd.ddpfPixelFormat.dwGBitMask    == pSprite->ddsd.ddpfPixelFormat.dwGBitMask &&
  784.             ddsd.ddpfPixelFormat.dwBBitMask    == pSprite->ddsd.ddpfPixelFormat.dwBBitMask )
  785.         {
  786.             // Make the DDS use the Sprite's pImageData for its surface contents
  787.             if( FAILED( hr = pScreenSurface->pDDS->SetSurfaceDesc( &pSprite->ddsd, 0 ) ) )
  788.                 return hr;
  789.  
  790.             return S_OK; // All done!  This DDS is ready to use.
  791.         }
  792.         // Otherwise, we can't share image data, and this system memory surface
  793.         // will be for this Screen only.
  794.     }
  795.  
  796.     // Copy image data from the Sprite to this ScreenSurface:
  797.     HDC hdc;
  798.     if( FAILED( hr = pScreenSurface->pDDS->GetDC( &hdc ) ) )
  799.         return hr;
  800.  
  801.     HDC     hdcImage;
  802.     HGDIOBJ hgdiobjOld;
  803.     DWORD   dwWidth = pSprite->rcSrc.right - pSprite->rcSrc.left;
  804.     DWORD   dwHeight = pSprite->rcSrc.bottom - pSprite->rcSrc.top;
  805.  
  806.     hdcImage   = CreateCompatibleDC(NULL);
  807.     hgdiobjOld = SelectObject(hdcImage, pSprite->hbmImage);
  808.     StretchBlt( hdc, 0, 0, dwWidth, dwHeight, 
  809.                 hdcImage, 0, 0, dwWidth, dwHeight, SRCCOPY );
  810.  
  811.     SelectObject( hdcImage, hgdiobjOld ); // restore previously selected object
  812.     DeleteDC( hdcImage );
  813.     TextOut( hdc, 0, 0, pSprite->szName, lstrlen(pSprite->szName) );
  814.     pScreenSurface->pDDS->ReleaseDC(hdc);
  815.  
  816.     if( ddsd.ddsCaps.dwCaps == DDSCAPS_VIDEOMEMORY )
  817.     {
  818.         if( FAILED( hr = pScreenSurface->pDDS->GetDC( &hdc ) ) )
  819.             return hr;
  820.  
  821.         wsprintf( sz, TEXT("Video memory copy") );
  822.         TextOut( hdc, 0, 20, sz, lstrlen(sz) );
  823.  
  824.         wsprintf( sz, TEXT("for %s"), pScreen->szDesc );
  825.         TextOut( hdc, 0, 40, sz, lstrlen(sz) );
  826.  
  827.         pScreenSurface->pDDS->ReleaseDC(hdc);
  828.     }
  829.     else if( pSprite->pImageData == NULL )
  830.     {
  831.         // No shared copy exists yet, so create one using data in this 
  832.         // system memory surface.
  833.         if( FAILED( hr = pScreenSurface->pDDS->GetDC( &hdc ) ) )
  834.             return hr;
  835.  
  836.         wsprintf(sz, TEXT("Shared System memory copy") );
  837.         TextOut(hdc, 0, 20, sz, lstrlen(sz) );
  838.         pScreenSurface->pDDS->ReleaseDC( hdc );
  839.  
  840.         // Copy image to pImageData so it can be shared among ScreenSurfaces:
  841.         if( SUCCEEDED( hr = pScreenSurface->pDDS->Lock( NULL, &ddsd, 
  842.                                                         DDLOCK_READONLY | DDLOCK_WAIT, 
  843.                                                         NULL ) ) )
  844.         {
  845.             pSprite->pImageData = new BYTE[ ddsd.lPitch * ddsd.dwHeight ];
  846.             if( pSprite->pImageData != NULL )
  847.             {
  848.                 // Store size, pitch, pixel format, and surface pointer info in Sprite
  849.                 pSprite->ddsd           = ddsd; 
  850.                 pSprite->ddsd.lpSurface = pSprite->pImageData;
  851.                 pSprite->ddsd.dwFlags   = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH | 
  852.                                           DDSD_PIXELFORMAT | DDSD_LPSURFACE;
  853.  
  854.                 // Copy image data from DDS's surface memory to Sprite's buffer
  855.                 CopyMemory( pSprite->pImageData, ddsd.lpSurface, 
  856.                             ddsd.lPitch * ddsd.dwHeight );
  857.             }
  858.  
  859.             pScreenSurface->pDDS->Unlock(NULL);
  860.  
  861.             if( pSprite->pImageData != NULL )
  862.             {
  863.                 // May as well make this ScreenSurface use the sharable copy too:
  864.                 if( FAILED( hr = pScreenSurface->pDDS->SetSurfaceDesc( &pSprite->ddsd, 0 ) ) )
  865.                     return hr;
  866.             }
  867.         }
  868.     }
  869.     else
  870.     {
  871.         // Shared copy exists, but attempt to use it failed (probably due to
  872.         // mismatched pixel format), so indicate that this as a non-shared sysmem copy:
  873.         if( FAILED( hr = pScreenSurface->pDDS->GetDC( &hdc ) ) )
  874.             return hr;
  875.  
  876.         wsprintf( sz, TEXT("System memory copy") );
  877.         TextOut( hdc, 0, 20, sz, lstrlen(sz) );
  878.         wsprintf( sz, TEXT("for %s"), pScreen->szDesc );
  879.         TextOut( hdc, 0, 40, sz, lstrlen(sz) );
  880.  
  881.         pScreenSurface->pDDS->ReleaseDC( hdc );
  882.     }
  883.     return S_OK;
  884. }
  885.  
  886.  
  887.  
  888.  
  889. //-----------------------------------------------------------------------------
  890. // Name: DestroyScreenSurfaces()
  891. // Desc: Destroys all ScreenSurfaces attached to the given Sprite.  This is
  892. //       called after restoring all surfaces (since image data may be lost) and
  893. //       when preparing to exit the program.
  894. //-----------------------------------------------------------------------------
  895. VOID DestroyScreenSurfaces( Sprite* pSprite )
  896. {
  897.     ScreenSurface* pScreenSurface;
  898.     ScreenSurface* pScreenSurfaceNext;
  899.  
  900.     pScreenSurface = pSprite->pScreenSurfaceFirst;
  901.     pSprite->pScreenSurfaceFirst = NULL;
  902.     while( pScreenSurface != NULL )
  903.     {
  904.         pScreenSurfaceNext = pScreenSurface->pScreenSurfaceNext;
  905.         pScreenSurface->pDDS->Release();
  906.  
  907.         delete pScreenSurface;
  908.  
  909.         pScreenSurface = pScreenSurfaceNext;
  910.     }
  911.  
  912.     if( pSprite->pImageData != NULL )
  913.     {
  914.         delete pSprite->pImageData;
  915.         pSprite->pImageData = NULL;
  916.     }
  917.  
  918.     ZeroMemory( &pSprite->ddsd, sizeof(pSprite->ddsd) );
  919. }
  920.  
  921.  
  922.  
  923.  
  924. //-----------------------------------------------------------------------------
  925. // Name: Cleanup()
  926. // Desc: Releases all resources allocated during this program.
  927. //-----------------------------------------------------------------------------
  928. VOID Cleanup( VOID )
  929. {
  930.     if( g_pSprite1 != NULL )
  931.     {
  932.         DestroyScreenSurfaces(g_pSprite1);
  933.         delete g_pSprite1;
  934.         g_pSprite1 = NULL;
  935.     }
  936.  
  937.     if( g_pSprite2 != NULL )
  938.     {
  939.         DestroyScreenSurfaces(g_pSprite2);
  940.         delete g_pSprite2;
  941.         g_pSprite2 = NULL;
  942.     }
  943.  
  944.     Screen* pScreen;
  945.     Screen* pScreenNext;
  946.  
  947.     pScreen = g_pScreenFirst;
  948.     g_pScreenFirst = NULL;
  949.  
  950.     while( pScreen != NULL )
  951.     {
  952.         pScreenNext = pScreen->pScreenNext;
  953.  
  954.         pScreen->pDDSBack->Release();
  955.         pScreen->pDDSFront->Release();
  956.         pScreen->pDD->RestoreDisplayMode();
  957.         pScreen->pDD->SetCooperativeLevel(g_hWnd, DDSCL_NORMAL);
  958.         pScreen->pDD->Release();
  959.  
  960.         delete pScreen;
  961.         pScreen = pScreenNext;
  962.     }
  963. }
  964.  
  965.  
  966.